package com.yfbao.horizon.schedule.tk.runner;

import ch.qos.logback.classic.LoggerContext;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.jvm.*;
import io.micrometer.core.instrument.binder.logging.Log4j2Metrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
public class MetricsBindRunner implements ApplicationRunner {

    @Resource
    private MeterRegistry registry;
    @Autowired(required = false)
    private List<MeterBinder> meterBinderList;

    @Autowired
    private ApplicationContext applicationContext;

    private static Set<String> metricsClass = new HashSet<>();
    private static Set<String> logMetricsClass = new HashSet<>();
    static {
        metricsClass.add("JvmGcMetrics");
        metricsClass.add("JvmMemoryMetrics");
        metricsClass.add("JvmThreadMetrics");
        metricsClass.add("ClassLoaderMetrics");
        metricsClass.add("UptimeMetrics");
        metricsClass.add("ProcessorMetrics");
        metricsClass.add("FileDescriptorMetrics");

        logMetricsClass.add("Log4j2Metrics");
        logMetricsClass.add("LogbackMetrics");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("meterBinderList size:"+meterBinderList.size());
        if (meterBinderList != null){
            meterBinderList.forEach(ctpMeterBinder -> {
                try {
                    String metricClassName = ctpMeterBinder.getClass().getSimpleName();
                    if(metricsClass.contains(metricClassName)){
                        metricsClass.remove(metricClassName);
                    }
                    if(logMetricsClass.contains(metricClassName)){
                        logMetricsClass.remove(metricClassName);
                    }
                    log.info("meterBinder name:"+metricClassName);
                    ctpMeterBinder.bindTo(registry);
                } catch (Exception e) {
                    log.error(ctpMeterBinder.getClass().getSimpleName() + "埋点绑定异常:", e);
                }
            });
        }
        if(logMetricsClass.size() == 2){
            bindLogMetric();
        }
        if(!metricsClass.isEmpty()){
            bindMetric();
        }
    }

    private void bindMetric(){
        for (String metric : metricsClass) {
            log.info("bind metric:"+metric+" by custom");
            if(metric.equals("JvmGcMetrics")){
                JvmGcMetrics metricsBean = new JvmGcMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("JvmMemoryMetrics")){
                JvmMemoryMetrics metricsBean = new JvmMemoryMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("JvmThreadMetrics")){
                JvmThreadMetrics metricsBean = new JvmThreadMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("ClassLoaderMetrics")){
                ClassLoaderMetrics metricsBean = new ClassLoaderMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("UptimeMetrics")){
                UptimeMetrics metricsBean = new UptimeMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("ProcessorMetrics")){
                ProcessorMetrics metricsBean = new ProcessorMetrics();
                metricsBean.bindTo(registry);
            }else if(metric.equals("FileDescriptorMetrics")){
                FileDescriptorMetrics metricsBean = new FileDescriptorMetrics();
                metricsBean.bindTo(registry);
            }
        }
    }

    private void bindLogMetric(){
        try {
            log.info("bind log metric by custom");
            if(conditionLog4JCoreLogger()){
                bindLog4j2Metrics();
            }else if(conditionLogback()){
                bindLogbackMetrics();
            }
        }catch (Throwable t){
            log.error("bind log metric error",t);
        }
    }

    private void bindLog4j2Metrics(){
        Log4j2Metrics bean = applicationContext.getBean(Log4j2Metrics.class);
        if(bean ==null){
            Log4j2Metrics log4j2Metrics = new Log4j2Metrics();
            log4j2Metrics.bindTo(registry);
            return;
        }
        bean.bindTo(registry);
    }
    private void bindLogbackMetrics(){
        LogbackMetrics bean = applicationContext.getBean(LogbackMetrics.class);
        if(bean ==null){
            LogbackMetrics logbackMetrics = new LogbackMetrics();
            logbackMetrics.bindTo(registry);
            return;
        }
        bean.bindTo(registry);
    }


    private boolean conditionLog4JCoreLogger(){
        try {
            Class.forName("org.apache.logging.log4j.core.LoggerContext");
            return true; // 如果条件满足，返回 true
        } catch (ClassNotFoundException e) {
            return false; // 如果条件不满足，返回 false
        }
    }

    private boolean conditionLogback(){
        try {
            ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
            ConditionMessage.Builder message = ConditionMessage.forCondition("LogbackLoggingCondition");
            if (loggerFactory instanceof LoggerContext) {
                return true;
            }
            return false;
        }catch (Throwable t){
            return false;
        }
    }


}
